package com.googlecode.sparda.commons.performance;


/**
 * Class to manage the caching of variable.
 * When the computing of something (typically a method call) is onerous,
 * so if that computing isn't critical in your application
 * then you can extends this abstract class to cache some value that calculus.
 * When extends this class, the method {@link #computeValue()} must be implemented,
 * the result of it will be cached.
 * The value is caching as soon as requested {@link #getValue()}.
 * To force to recalculate the value, you can use {@link #expireCache()}.
 * @author Fabio Grucci (<i>alias</i> <b>Phoenix</b>)
 * @version 1.0
 * @since 1.0
 */
public abstract class CachedValue<T> {

	/**
	 * The cached value result of hard computing.
	 */
	private T cachedValue;

	/**
	 * The last updating time of the cached value.
	 */
	private long lastUpdateTime;
	
	/**
	 * Booleans that specify if the value {@link #cachedValue} must be recalculate.
	 */
	boolean expired;
	
	/**
	 * Build a {@link CachedValue} object.
	 */
	public CachedValue() {
		this.lastUpdateTime = -1;
		this.expired = true;
	}
	
	/**
	 * Return the {@link #expired} boolean value.
	 * @return {@link #expired} value <code>true</code> if cached value is validate, <code>false</code> otherwise
	 */
	public boolean isExpired() {
		return this.expired;
	}

	/**
	 * Method called to set the {@link #isExpired()} value.
	 * Extend this method if you want to capture loading or invalidating cache event.
	 * @param expired the value to set
	 */
	protected void setExpired(boolean expired) {
		this.expired = expired;
	}
	
	/**
	 * Method that invalidate the cached value, the value will be computed 
	 * in the next {@link #getValue()} call.
	 * Invoke this method if you want to invalidate previous cached value.
	 */
	public void expireCache() {
		this.setExpired(true);
	}

	/**
	 * Return the computed/cached value.
	 * If not in cache, or cache is invalidating then: 
	 * <ul>
	 * <li>the value is computed (calling {@link #computeValue()})</li>
	 * <li>the cache updated whit new computed value (calling {@link #setValue(Object)})</li>
	 * </ul>
	 * @return The cached value
	 */
	public final T getValue() {
		if(this.isExpired())
			this.setValue(this.computeValue());
		return this.cachedValue;
	}
	
	/**
	 * Method to set the cached value.
	 * This methods provides to save the value in cache and 
	 * to set the {@link #expired} value.
	 * @param newCachedValue the new value to cache
	 */
	protected void setValue(T newCachedValue) {
		this.setExpired(false);
		this.cachedValue = newCachedValue;
		this.lastUpdateTime = System.currentTimeMillis();
	}
	
	/**
	 * Return the last updating time in milliseconds of the cached value.
	 * A negative number if the value is never computed.
	 * @return The last update time in milliseconds, -1 if the value is never loaded
	 */
	public long getLastUpdateTime() {
    	return lastUpdateTime;
    }
	
	/**
	 * This method must return the value to cache.
	 * Typically is an onerous method.
	 * @return The computed value.
	 */
	protected abstract T computeValue();
	
	/*
	 * (non Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return this.cachedValue.toString();
	}
	
}